// Mzz 
// 2024-5-30

#include "GxIAPI.h"
#include "DxImageProc.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>
#include <mutex>
#include <queue>
#include <condition_variable>
typedef unsigned char BYTE;
using namespace cv;
using namespace std;

// 读取大恒相机视频帧，并保存。由于图像过于大，保存图像影响速度，所以另起线程保存图像。
// 定义一个结构体来保存图像帧和文件名
struct FrameData {
    cv::Mat frame;
    std::string filename;
};

// 全局队列来存储待保存的图像帧
std::queue<FrameData> frameQueue;
std::mutex queueMutex;
std::condition_variable condVar;
bool finished = false;

// 线程函数，用于从队列中取出图像帧并保存
void saveFramesThread() {
    while (true) {
        std::unique_lock<std::mutex> lock(queueMutex);
        condVar.wait(lock, [] { return !frameQueue.empty() || finished; });

        if (finished && frameQueue.empty()) break;

        while (!frameQueue.empty()) {
            FrameData data = frameQueue.front();
            frameQueue.pop();
            lock.unlock(); // 解锁以允许其他线程访问队列

            // 保存图像
            cout << data.filename << endl;
            cv::imwrite(data.filename, data.frame);

            lock.lock(); // 再次锁定以安全地检查队列状态
        }
    }
}


int main(int argc, char* argv[])
{    

    GX_STATUS status = GX_STATUS_SUCCESS;
    GX_DEV_HANDLE hDevice = NULL;
    uint32_t nDeviceNum = 0;
    //初始化库
    status = GXInitLib();
    if (status != GX_STATUS_SUCCESS)
    {
        return 0;
    }
    //枚举设备列表
    status = GXUpdateDeviceList(&nDeviceNum, 1000);

    if ((status != GX_STATUS_SUCCESS) || (nDeviceNum <= 0))
    {
        return 0;
    }
    cout << "Open success!"<<endl;

    uint32_t entrynums;
    status = GXGetEnumEntryNums(hDevice, GX_ENUM_PIXEL_FORMAT, &entrynums);
    cout << entrynums << endl;
    
    int64_t nPixelFormat = 0;
    status = GXGetEnum(hDevice, GX_ENUM_PIXEL_FORMAT, &nPixelFormat);

    status = GXSetFloat(hDevice, GX_FLOAT_ACQUISITION_FRAME_RATE, 10.0);  // 设定帧率
    status = GXSetEnum(hDevice, GX_ENUM_EXPOSURE_AUTO, GX_EXPOSURE_AUTO_CONTINUOUS);   // 设定曝光值
    status = GXSetEnum(hDevice,GX_ENUM_AWB_LAMP_HOUSE, GX_AWB_LAMP_HOUSE_FLUORESCENCE);
    
    //打开第一个设备
    status = GXOpenDeviceByIndex(1, &hDevice);

    std::thread frameSaver(saveFramesThread);

    if (status == GX_STATUS_SUCCESS)
    {
        //定义 GXDQBuf 的传入参数
        PGX_FRAME_BUFFER pFrameBuffer;
        //开始循环采集图像
        while(true){
            status = GXStreamOn(hDevice);
            if (status == GX_STATUS_SUCCESS)
            {
                //调用 GXDQBuf 取一帧图像
                status = GXDQBuf(hDevice, &pFrameBuffer, 1000);
                if (status == GX_STATUS_SUCCESS)
                {
                    if (pFrameBuffer->nStatus == GX_FRAME_STATUS_SUCCESS)
                    {
                        //图像获取成功
                        //对图像进行处理...
                        void* rowdata = pFrameBuffer->pImgBuf;
                        int width = pFrameBuffer->nWidth;
                        int height = pFrameBuffer->nHeight;

                        cout <<"pFrameBuffer size: "<< width <<"x"<<height<<endl;
                      
                        BYTE *pRGB24Buf = new BYTE[width * height * 3]; //输出图像 RGB 数据

                        if (pRGB24Buf == NULL)
                        {
                        return 0;
                        }
                        else
                        {
                        memset(pRGB24Buf,0,width * height * 3 * sizeof(BYTE)); //缓冲区初始化
                        }
                        DX_BAYER_CONVERT_TYPE cvtype = RAW2RGB_NEIGHBOUR; //选择插值算法
                        DX_PIXEL_COLOR_FILTER nBayerType = BAYERRG; //选择图像 Bayer 格式
                        bool bFlip = false;
                        
                        VxInt32 DxStatus = DxRaw8toRGB24(rowdata, pRGB24Buf, width, height, cvtype, nBayerType, bFlip);
                       
                        if (DxStatus != DX_OK)
                        {
                        if (pRGB24Buf != NULL)
                        {
                        delete []pRGB24Buf;
                        pRGB24Buf = NULL;
                        }
                        return 0;
                        }
                        
                       // cv::Mat img(height, width, CV_32FC3, pRGB24Buf);
                        // 生成文件名，包含毫秒级时间戳以确保唯一
                        auto now = std::chrono::system_clock::now();
                        auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
                        auto epoch = now_ms.time_since_epoch();
                        auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
                        long duration = value.count();

                        std::time_t now_c = std::chrono::system_clock::to_time_t(now);
                        std::tm now_tm = *std::localtime(&now_c);

                        std::stringstream ss;  
                        // ***** 改为自己保存图像的路径
                        ss << "************" << std::put_time(&now_tm, "%Y%m%d_%H%M%S") << "_" << (duration % 1000) << ".png";
                        std::string filename = ss.str();                        
                        Mat img(height, width, CV_8UC3, pRGB24Buf);
                        {
                            std::lock_guard<std::mutex> lock(queueMutex);
                            frameQueue.emplace(FrameData{img, filename});
                        }
                        condVar.notify_one(); 
                    }
                    //调用 GXQBuf 将图像 buf 放回库中继续采图
                    status = GXQBuf(hDevice, pFrameBuffer);
                }  
            } 
        } //停采
        status = GXStreamOff(hDevice); 
    } status = GXCloseDevice(hDevice); 
    status = GXCloseLib(); 

    // 通知保存线程结束
    {
        std::lock_guard<std::mutex> lock(queueMutex);
        finished = true;
    }
    condVar.notify_one();

    // 等待保存线程结束
    if (frameSaver.joinable()) {
        frameSaver.join();
    }
    return 0;
}
